#pragma once

#include <functional>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <sys/epoll.h> 
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <cstdint>
#include <cstdio>
#include <atomic>

#include "log/log.h"

class SmartCamera
{
    struct CamFrame_t {
    void *start;
        size_t length;
    };

    struct CamSource_t {
        int32_t fd;
        uint32_t bufcnt;
        enum v4l2_buf_type type;
        struct CamFrame_t *buf;
    };

public:
    SmartCamera() {}

    SmartCamera(const char *device, uint32_t bufcnt, uint32_t width, uint32_t height, uint32_t fmt) {
        CameraSourceInit(device, bufcnt, width, height, fmt);
    }

    ~SmartCamera() {
        deinit();
    };

    void init(const char *device, uint32_t bufcnt, uint32_t width, uint32_t height, uint32_t fmt) {
        CameraSourceDeinit();
        CameraSourceInit(device, bufcnt, width, height, fmt);
    }

    void demo_init() {
        CameraSourceDeinit();
        CameraSourceInit("/dev/video0", 5, 1920, 1080, V4L2_PIX_FMT_UYVY);
    }

    void deinit() {
        CameraSourceDeinit();
    }

    int recv_frame(uint8_t *buf, uint32_t len) {
        int idx, ret;
        CamSource_t *ctx = &m_camSource;

        idx = CameraSourceGetFrame();
        if (idx >= 0 && idx < ctx->bufcnt) {
            ret = CameraFrameToBuf(idx, (uint8_t *)ctx->buf[idx].start, ctx->buf[idx].length);
            if (ret < 0) return ret;
            CameraSourcePutFrame(idx);
        }
        
        return ret;
    }

    void loop_recv_frame(std::function<void(uint8_t *, uint32_t)> callback) {
        CameraFrameToBufCallBack(callback);
    }

    void stop() {
        m_loop_run = false;
    }

private:
    int CameraSourceInit(const char *device, uint32_t bufcnt, uint32_t width, uint32_t height, uint32_t fmt);
    int CameraSourceDeinit(void);

    int CameraSourceGetFrame(void);
    int CameraSourcePutFrame(int32_t idx);
    int CameraFrameToBuf(int32_t idx, uint8_t *buf, uint32_t len);

    int CameraFrameToBufCallBack(std::function<void(uint8_t *, uint32_t)> callback);

private:
    struct CamSource_t m_camSource;
    const int FMT_NUM_PLANES{1};

    std::atomic_bool m_loop_run{false};
};
